package ptij.ADSL.pn.ATUR;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.actor.*;

public class IDFT extends TypedAtomicActor {

    public TypedIOPort subChannel[];
    public TypedIOPort output;

    public IDFT(TypedCompositeActor container, String name) throws
        NameDuplicationException, IllegalActionException {
        super(container, name);
	
	subChannel = new TypedIOPort[31];
	for(int i = 0;i < 31;i++) {
	    subChannel[i] = new TypedIOPort(this, "subChannel_" + i,
	        true, false);
	    subChannel[i].setTypeEquals(DoubleToken.class);
	}
	
	output = new TypedIOPort(this, "output", false, true);
	output.setTypeEquals(DoubleToken.class);
    }

    public void fire() throws IllegalActionException {        	
	
	_data[0] = 0;
	_data[1] = 0;
	_data[64] = 0;
	_data[65] = 0;
        for(int i = 1;i < 32;i++) {
	    DoubleToken dt = (DoubleToken) subChannel[i - 1].get(0);
	    _data[2*i] = dt.doubleValue();
	    _data[128 - 2*i] = _data[2*i];
	    dt = (DoubleToken) subChannel[i - 1].get(0);
	    _data[2*i + 1] = dt.doubleValue();
	    _data[129 - 2*i] = -1 * _data[2*i + 1];	    	    
	}

	_idft();
	
	for(int i = 0;i < 64;i++) {
	    output.broadcast(new DoubleToken(_data[2*i]));
	}
    }
    
    public void initialize() throws IllegalActionException {
        super.initialize();
	_data = new double[128];
    }
    
    private void _idft() {
        transform_internal(+1);  
        double norm = 1/((double) 64);
        for(int i = 0; i < 128;i += 2) _data[i] *= norm;
    }

    private void transform_internal(int direction) {
	int n = 64;
	int logn = 6;

        bitreverse();

        for(int bit = 0, dual = 1; bit < logn; bit++, dual *= 2) {
            double w_real = 1.0;
            double w_imag = 0.0;

            double theta = 2.0 * direction * Math.PI / (2.0 * (double) dual);
            double s = Math.sin(theta);
            double t = Math.sin(theta / 2.0);
            double s2 = 2.0 * t * t;
       
            for (int b = 0; b < n; b += 2 * dual) {
                int i = 2*b;
                int j = 2*(b + dual);

                double wd_real = _data[j];
                double wd_imag = _data[j+1];
          
                _data[j] = _data[i] - wd_real;
                _data[j+1] = _data[i+1] - wd_imag;
                _data[i] += wd_real;
                _data[i+1] += wd_imag;
            }
      
            for (int a = 1; a < dual; a++) {
                { 
                    double tmp_real = w_real - s * w_imag - s2 * w_real;
                    double tmp_imag = w_imag + s * w_real - s2 * w_imag;
                    w_real = tmp_real;
                    w_imag = tmp_imag;
                }
                
		for(int b = 0; b < n; b += 2 * dual) {
                    int i = 2*(b + a);
                    int j = 2*(b + a + dual);

                    double z1_real = _data[j];
                    double z1_imag = _data[j+1];
              
                    double wd_real = w_real * z1_real - w_imag * z1_imag;
                    double wd_imag = w_real * z1_imag + w_imag * z1_real;

                    _data[j] = _data[i] - wd_real;
                    _data[j+1] = _data[i+1] - wd_imag;
                    _data[i]  += wd_real;
                    _data[i+1]+= wd_imag;
                }
            }
        }
    }

    private void bitreverse() {
	int i = 0; 
	int j = 0;
    
        for(; i < 63; i++) {

            int ii = i << 1;
            int jj = j << 1;

            int k = 64 >> 1;

            if (i < j) {
                double tmp_real = _data[ii];
                double tmp_imag = _data[ii+1];
                _data[ii] = _data[jj];
                _data[ii+1] = _data[jj+1];
                _data[jj] = tmp_real;
                _data[jj+1] = tmp_imag;
	     }
             while(k <= j) {
		j -= k;
                k >>= 1 ; 
	     }
             j += k;
         }
    }
     
    private double _data[];
}
